import * as React from "react";
import Cascader from "./Cascader";
import { CascaderOptionType } from "./Menus";
import classNames from "classnames";
import omit from "omit.js";
import KeyCode from "rc-util/lib/KeyCode";
import Input from "@zcy/doraemon/lib/input";
import Icon from "@zcy/doraemon/lib/icon";
import Popover from "@zcy/doraemon/lib/popover";
import Tag from "@zcy/doraemon/lib/tag";

export type CascaderExpandTrigger = "click" | "hover";

export interface AddressCascaderProps {
  /** 可选项数据源 */
  options: CascaderOptionType[];
  /** 默认的选中项 */
  defaultValue?: string[];
  /** 指定选中项 */
  value?: string[];
  /** 选择完成后的回调 */
  onChange?: (
    value: string[],
    selectedOptions?: CascaderOptionType,
    allSelectedOptions?: CascaderOptionType[]
  ) => void;
  /** 选择后展示的渲染函数 */
  displayRender?: (
    label: string[],
    selectedOptions?: CascaderOptionType[]
  ) => React.ReactNode;
  /** 自定义样式 */
  style?: React.CSSProperties;
  /** 自定义类名 */
  className?: string;
  /** 自定义浮层类名 */
  popupClassName?: string;
  /** 浮层预设位置：`bottomLeft` `bottomRight` `topLeft` `topRight` */
  popupPlacement?: string;
  /** 输入框占位文本*/
  placeholder?: string;
  /** 输入框大小，可选 `large` `default` `small` */
  size?: string;
  /** 禁用*/
  disabled?: boolean;
  /** 是否支持清除*/
  allowClear?: boolean;
  notFoundContent?: React.ReactNode;
  /** 次级菜单的展开方式，可选 'click' 和 'hover' */
  expandTrigger?: CascaderExpandTrigger;
  /** 当此项为 true 时，点选每级菜单选项值都会发生变化 */
  changeOnSelect?: boolean;
  /** 浮层可见变化时回调 */
  onPopupVisibleChange?: (popupVisible: boolean) => void;
  prefixCls?: string;
  inputPrefixCls?: string;
  getPopupContainer?: (triggerNode?: Element) => HTMLElement;
  popupVisible?: boolean;
}

export interface AddressCascaderState {
  inputFocused: boolean;
  tooltip: JSX.Element[];
  tooltipVisible: boolean;
  value: CascaderOptionType[];
  options: CascaderOptionType[];
  popupVisible: boolean | undefined;
}

export default class AddressCascader extends React.Component<
  AddressCascaderProps,
  AddressCascaderState
> {
  static defaultProps = {
    prefixCls: "ant-cascader",
    inputPrefixCls: "ant-input",
    placeholder: "请选择",
    popupPlacement: "bottomLeft",
    options: [],
    disabled: false,
    allowClear: true,
    notFoundContent: "暂无数据"
  };

  // cachedOptions: CascaderOptionType[];

  private input: any;

  constructor(props: AddressCascaderProps) {
    super(props);
    const params = this.initOptions(props, props.value, props.defaultValue);
    this.state = {
      value: params.value, // props.value || props.defaultValue || [],
      options: params.options,
      tooltip: params.tooltip,
      tooltipVisible: false,
      inputFocused: false,
      popupVisible: props.popupVisible,
    };
    this.handleToolTipVisibleChange = this.handleToolTipVisibleChange.bind(
      this
    );
  }

  componentWillReceiveProps(nextProps: AddressCascaderProps) {
    if ("value" in nextProps) {
      let deepEquals = true;
      let value = this.state.value;
      if (nextProps.value && nextProps.value.length === value.length) {
        for (const key in value) {
          if (nextProps.value.indexOf(value[key].value) === -1) {
            deepEquals = false;
            break;
          }
        }
      } else {
        deepEquals = false;
      }
      if (!deepEquals) {
        const params = this.initOptions(nextProps, nextProps.value);
        this.setState({
          value: params.value,
          options: params.options,
          tooltip: params.tooltip
        });
      }
    }
    if ("popupVisible" in nextProps) {
      this.setState({ popupVisible: nextProps.popupVisible });
    }
  }

  initOptions = (
    props: AddressCascaderProps,
    oValue?: any[],
    defaultValue?: any[]
  ) => {
    let options: CascaderOptionType[] = [];
    let value: CascaderOptionType[] = [];
    let tooltip;
    if (props.options) {
      options = this.generateOptions(props.options);
    } else {
      options = this.state.options;
    }
    if (oValue || defaultValue) {
      this.generateValues(value, options, oValue || defaultValue);
    }
    this.setOptionsState(options, value);
    tooltip = this.getToolTip(value);
    return {
      options,
      value,
      tooltip
    };
  };

  handleChange = (targetOption: CascaderOptionType, checkState: boolean) => {
    let value: CascaderOptionType[] = [];
    let options = this.state.options;
    let tooltip;

    // 设置弹出框checkbox状态
    targetOption.checked = checkState;
    // 根据checkbox状态计算value
    this.getValueByOption(value, options);

    tooltip = this.getToolTip(value);
    this.setState(
      {
        options: this.state.options,
        value: value,
        tooltip: tooltip
      },
      () => {
        if (this.props.onChange) {
          let valueArr = value.map(item => {
            return item.value;
          });
          let optionArr = value.map(item => {
            return {
              value: item.value,
              label: item.label,
              level: item.level
            };
          });
          this.props.onChange(valueArr, targetOption, optionArr);
        }
      }
    );
  };

  handlePopupVisibleChange = (popupVisible: boolean) => {
    if (!("popupVisible" in this.props)) {
      this.setState({
        popupVisible,
        inputFocused: popupVisible,
        // inputValue: popupVisible ? this.state.inputValue : ""
      });
    }

    const onPopupVisibleChange = this.props.onPopupVisibleChange;
    if (onPopupVisibleChange) {
      onPopupVisibleChange(popupVisible);
    }
  };

  handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
    if (e.keyCode === KeyCode.BACKSPACE) {
      e.stopPropagation();
    }
  };

  getValueByOption(value: CascaderOptionType[], options: CascaderOptionType[]) {
    options.forEach(item => {
      if (item.checked) {
        value.push(item);
      }
      if (item.children && item.children.length > 0) {
        this.getValueByOption(value, item.children);
      }
    });
  }

  setValue = (value: string[]) => {
    const onChange = this.props.onChange;
    if (onChange) {
      onChange(value);
    }
  };

  setCheckState(selectedChange: any, checkState: boolean) {
    if (selectedChange.children && selectedChange.children.length > 0) {
      this.setCheckState(selectedChange.children, checkState);
    } else {
      selectedChange.checked = checkState;
    }
  }

  getTagList() {
    let pop: Array<JSX.Element> = [];
    let { value } = this.state;
    const { prefixCls } = this.props;
    if (value.length > 4) {
      for (let i = 0; i < value.length; i++) {
        if (i < 3) {
          pop.push(
            <Tag
              key={value[i].value}
              className={`${prefixCls}-picker-multi-label-tag`}
            >
              {value[i].label}
            </Tag>
          );
        } else {
          pop.push(
            <Tag
              key={value[i].value}
              className={`${prefixCls}-picker-multi-label-tag`}
            >
              {"···"}
            </Tag>
          );
          break;
        }
      }
    } else {
      this.state.value.forEach((item, index: number) => {
        pop.push(
          <Tag key={index} className={`${prefixCls}-picker-multi-label-tag`}>
            {item.label}
          </Tag>
        );
      });
    }
    return pop;
  }

  getToolTip(value: CascaderOptionType[]) {
    let tooltip: JSX.Element[] = [];
    if (value.length >= 1) {
      value.forEach((item, index) => {
        let splitStr = "、";
        if (index === value.length - 1) {
          splitStr = "";
        }
        if (index > 0 && index % 5 === 0) {
          tooltip.push(
            <span key={`tooltip-${index}`}>
              {item.label}
              {splitStr}
            </span>
          );
        } else {
          tooltip.push(
            <span key={`tooltip-${index}`}>
              {item.label}
              {splitStr}
            </span>
          );
        }
      });
    }
    return tooltip;
  }

  handleToolTipVisibleChange(visible: boolean) {
    if (this.state.tooltip.length >= 1) {
      this.setState({
        tooltipVisible: visible
      });
    } else {
      this.setState({
        tooltipVisible: false
      });
    }
  }

  clearSelection = (e?: React.MouseEvent<HTMLElement>) => {
    e && e.preventDefault();
    e && e.stopPropagation();
    let options = this.state.options;
    let value = this.state.value;
    this.clearOptionSelect(options);
    value.splice(0, value.length);
    this.setState({
      options: options,
      value: value,
      tooltip: this.getToolTip(value),
      tooltipVisible: false
    });
    this.setValue([]);
    e && this.handlePopupVisibleChange(false);
  };

  clearOptionSelect(options: CascaderOptionType[]) {
    options.forEach(item => {
      item.checked = false;
      item.indeterminate = false;
      if (item.children && item.children.length > 0) {
        this.clearOptionSelect(item.children);
      }
    });
  }

  /**
   * 转换props状态为内部options状态
   * @param {} options
   */
  generateOptions(options: CascaderOptionType[]): CascaderOptionType[] {
    let stateOptions: CascaderOptionType[] = [];
    // options添加level属性
    const setLevel = (options?: CascaderOptionType[]) => {
      var level = 1;
      (function a(arr) {
        if (!arr || !arr.length) {
          return;
        }
        for (var i = 0; i < arr.length; i++) {
          arr[i].level = level;
          if (arr[i].children) {
            level++;
            a(arr[i].children);
            level--;
          }
        }
      })(options);
    };

    /**
     * 设置options所有子孙节点的父指针
     * @param options
     */
    const setOptionsParent = (options: CascaderOptionType[]) => {
      options.map(item => {
        if (item.children && item.children.length) {
          item.children.map(childItem => {
            childItem.parent = item;
          });
          setOptionsParent(item.children);
        }
      });
    };
    if (options && options.length > 0) {
      stateOptions = JSON.parse(JSON.stringify(options));
      setLevel(stateOptions);
      setOptionsParent(stateOptions);
    }
    return stateOptions;
  }

  generateValues(
    valueOptions: CascaderOptionType[],
    options: CascaderOptionType[],
    value?: string[]
  ) {
    if (value && value.length > 0) {
      options.map(item => {
        if (value.indexOf(item.value) !== -1) {
          valueOptions.push(item);
        } else {
          if (item.children && item.children.length > 0) {
            this.generateValues(valueOptions, item.children, value);
          }
        }
      });
    }
  }

  /**
   * 根据value设置所有节点的选中状态
   * @param options
   * @param value
   */
  setOptionsState(options: CascaderOptionType[], value: CascaderOptionType[]) {
    if (value && value.length > 0) {
      options.map(item => {
        if (value.indexOf(item) !== -1) {
          item.checked = true;
        } else {
          if ("checked" in item) {
            delete item.checked;
          }
          if (item.children && item.children.length > 0) {
            this.setOptionsState(item.children, value);
          }
        }
      });
    }
  }

  focus() {
    this.input.focus();
  }

  blur() {
    this.input.blur();
  }

  saveInput = (node: Input) => {
    this.input = node;
  };

  render() {
    const { props, state } = this;
    const {
      prefixCls,
      inputPrefixCls,
      placeholder,
      size,
      disabled,
      className,
      style,
      allowClear,
      popupPlacement,
      popupClassName,
      ...otherProps
    } = props;
    const value = state.value;

    const sizeCls = classNames({
      [`${inputPrefixCls}-lg`]: size === "large",
      [`${inputPrefixCls}-sm`]: size === "small"
    });
    const clearIcon =
      (allowClear && !disabled && value.length > 0)  ? (
        <Icon
          type="cross-circle"
          className={`${prefixCls}-picker-clear`}
          onClick={this.clearSelection}
        />
      ) : null;
    const arrowCls = classNames({
      [`${prefixCls}-picker-arrow`]: true,
      [`${prefixCls}-picker-arrow-expand`]: state.popupVisible
    });
    const pickerCls = classNames(className, `${prefixCls}-picker`, {
      [`${prefixCls}-picker-disabled`]: disabled,
      [`${prefixCls}-picker-${size}`]: !!size
    });

    // Fix bug of https://github.com/facebook/react/pull/5004
    // and https://fb.me/react-unknown-prop
    const inputProps = omit<any, string>(otherProps, [
      "onChange",
      "options",
      "popupPlacement",
      "displayRender",
      "onPopupVisibleChange",
      "changeOnSelect",
      "expandTrigger",
      "popupVisible",
      "getPopupContainer",
      "popupClassName",
      "filterOption",
      "renderFilteredOption",
      "sortFilteredOption",
      "notFoundContent"
    ]);

    let options = this.state.options;

    const input = (
      <span style={style} className={pickerCls}>
        <span className={`${prefixCls}-picker-multi-label`}>
          {this.getTagList()}
        </span>
        <Input
          {...inputProps}
          ref={this.saveInput}
          prefixCls={inputPrefixCls}
          placeholder={value && value.length > 0 ? undefined : placeholder}
          className={`${prefixCls}-input ${sizeCls}`}
          value=""
          disabled={disabled}
          readOnly={true}
          autoComplete="off"
          onKeyDown={this.handleKeyDown}
        />
        {clearIcon}
        <Icon type="xiala" zcy className={arrowCls} />
      </span>
    );
    const overlayStyle = {
      maxWidth: "260px"
    };
    // hover内容添加高度限制
    const popoverContent = (
      <div style={{ maxHeight: 200, overflow: "scroll" }}>{state.tooltip}</div>
    );
    return (
      <Popover
        content={popoverContent}
        visible={state.tooltipVisible}
        onVisibleChange={this.handleToolTipVisibleChange}
        mouseEnterDelay={1}
        overlayStyle={overlayStyle}
        getPopupContainer={props.getPopupContainer}
      >
        <Cascader
          prefixCls={prefixCls}
          popupPlacement={popupPlacement}
          popupClassName={popupClassName}
          options={options}
          value={value}
          popupVisible={state.popupVisible}
          onPopupVisibleChange={this.handlePopupVisibleChange}
          onChange={this.handleChange}
          clearSelection={this.clearSelection}
        >
          {input}
        </Cascader>
      </Popover>
    );
  }
}
